/**
 * @file
 * @author dousha
 * @date 2020
 */

#include "stm32f4xx.h" // 包含基本的外设定义

#include "init.h"

/**
 * 设置系统时钟和其他核心外设。
 * 
 * @remark 外围外设不在这里设置。见 kernel/peripheral/ 下关于外围外设的设置。
 * 
 * @invokedBy{startup.s}
 */
void system_init() { // 在 startup.s 中调用的设置函数
	RCC->CR |= 0x00000001u; // HSION = 1, 启用内部高频率晶振
	RCC->CFGR = 0; // 清零时钟输出与分频配置
	RCC->CR &= 0xFEF6FFFFu; // HSEON = 0, CSSON = 0, PLLON = 0, 关闭外部晶振、时钟监控和 PLL 以便配置
	RCC->PLLCFGR = 0x24003010u; // 初始化 PLL 配置寄存器
	RCC->CR &= 0xFFFBFFFFu; // HSEBYP = 0, 不绕过外部晶振
	RCC->CIR = 0; // 关闭所有时钟中断
	SCB->VTOR = 0; // 中断向量表在 0x0000_0000, 代码段

	volatile uint32_t status = 0;
	RCC->CR |= 0x00010000u; // HSEON = 1, 启用外部晶振
	do {
		status = RCC->CR & 0x00020000u;
	} while (status == 0); // 等待外部时钟启动
	RCC->APB1ENR |= 0x10000000u; // 启动电源外设时钟
	PWR->CR |= 0x00004000u; // 使用电源模式 1
	RCC->CFGR &= 0xffffff0fu; // AHB 不分频
	RCC->CFGR |= 0x00009400u; // APB2 使用 2 分频、APB1 使用 4 分频
	RCC->PLLCFGR = (Q << 24u) | (1u << 22u) | (P << 16u) | (N << 6u) | M; // PLL 配置
	RCC->CR |= 0x01000000u; // PLLON = 1, 启动 PLL
	do {
		status = RCC->CR & 0x02000000u;
	} while (status == 0); // 等待 PLL 启动
	RCC->CFGR |= 0x00000002u; // 选择 PLL 作为系统时钟源
	do {
		status = RCC->CFGR & 0x00000008u;
	} while (status == 0); // 等待系统时钟源切换
	SCB->CPACR = 0x00f00000u; // 启用协处理器
	FLASH->ACR = 0x00000705u; // DCEN = 1, ICEN = 1, PRFEN = 1, LATENCY[2:0] = 5, 使用数据缓存、指令缓存、预取、延迟 5 个指令周期
}
